﻿#include <thirdparty/fist-log.h>

#include "network_interface.h"
#include "network_node.h"

using namespace asio;

CNetworkNode::CNetworkNode(INetworkManager* network_manager, io_service* io_service):
    m_network_manager(network_manager),
    m_socket(new ip::tcp::socket(*io_service)),
    m_valid(false),
    m_type(NETWORK_NODE_TYPE_SERVER) { }

CNetworkNode::~CNetworkNode()
{
    if (m_socket) {
        delete m_socket;
        m_socket = nullptr;
    }
    while (!m_read_data_buf.empty()) {
        char* buf = m_read_data_buf.front();
        m_read_data_buf.pop();
        delete buf;
    }
    while (!m_write_data_buf.empty()) {
        char* buf = m_write_data_buf.front();
        m_write_data_buf.pop();
        delete buf;
    }
}

const bool& CNetworkNode::valid()
{
    return m_valid;
}

void CNetworkNode::set_valid(bool is_valid)
{
    m_valid = is_valid;
}

const int& CNetworkNode::type()
{
    return m_type;
}

ip::tcp::socket& CNetworkNode::sock()
{
    return *m_socket;
}

void CNetworkNode::Destroy()
{
    if (m_socket->is_open()) {
        m_socket->shutdown(socket_base::shutdown_both);
        m_socket->close();
    }
}

char* CNetworkNode::GetReadCanUseBuf()
{
    if (m_read_data_buf.empty()) {
        for (int i = 0; i < 10; ++i) {
            auto buf = new char[m_network_manager->cfg()->max_raw_pack_size];
            m_read_data_buf.push(buf);
        }
    }
    char* buf = m_read_data_buf.front();
    m_read_data_buf.pop();
    return buf;
}

void CNetworkNode::RecycleReadBuf(char* buf)
{
    m_read_data_buf.push(buf);
}

char* CNetworkNode::GetWriteCanUseBuf()
{
    if (m_write_data_buf.empty()) {
        for (int i = 0; i < 10; ++i) {
            auto buf = new char[m_network_manager->cfg()->max_raw_pack_size];
            m_write_data_buf.push(buf);
        }
    }
    char* buf = m_write_data_buf.front();
    m_write_data_buf.pop();
    return buf;
}

void CNetworkNode::RecycleWriteBuf(char* buf)
{
    m_write_data_buf.push(buf);
}


int CNetworkNode::SendOneMessage(const std::string& msg)
{
    if (!m_valid) {
        return NETWORK_CONNECT_INVALID;
    }
    RawPack pack;
    if (m_type == NETWORK_NODE_TYPE_MOBILE) {
        pack.set_type(RAWPACKTYPE_CLIENT);
    } else {
        pack.set_type(RAWPACKTYPE_RPC);
    }
    pack.set_length(static_cast<uint32_t>(msg.length()));
    pack.set_value(msg);
    char* cache = GetWriteCanUseBuf();
    // LOG_DEBUG("send new message");
    const size_t pack_size = pack.ByteSizeLong();
    if (!pack.SerializeToArray(cache, static_cast<int>(pack_size))) {
        LOG_ERROR("SerializeToArray fail!");
        return NETWORK_SERIALIZE_FAIL;
    }
    auto write_handler = [=, this]<typename ErrorCode, typename WriteCount>(ErrorCode&& code, WriteCount&& count) {
        // LOG_DEBUG("send new message back");
        if (code) {
            LOG_ERROR("async_send fail. err:{}", code.value());
            m_valid = false;
        }
        if (pack_size != count) {
            LOG_WARN("send message not complete! send:[{} {}]", pack_size, count);
        }
        RecycleWriteBuf(cache);
    };
    async_write(*m_socket, buffer(cache, pack_size), write_handler);
    return NETWORK_OK;
}

int CNetworkNode::PostRead(char* cache /* = nullptr */, size_t offset /* = 0 */)
{
    if (!m_valid) {
        return NETWORK_CONNECT_INVALID;
    }
    if (m_network_manager->cfg()->max_raw_pack_size < offset) {
        return NETWORK_PACK_TOO_LARGE;
    }
    char* buf = nullptr;
    if (cache == nullptr) {
        buf = GetReadCanUseBuf();
        cache = buf;
    } else {
        buf = cache + offset;
    }
    // LOG_DEBUG("post read start");
    auto read_handler = [=, this]<typename ErrorCode, typename ReadCount>(ErrorCode&& code, ReadCount&& count) {
        // LOG_DEBUG("post read back");
        if (code) {
            LOG_ERROR("async_read_some fail. err:{}", code.value());
            m_valid = false;
            RecycleReadBuf(cache);
        } else {
            int retCode = NETWORK_FAIL;
            if (count + offset <= m_network_manager->cfg()->min_raw_pack_size) {
                retCode = PostRead(cache, count + offset);
            } else if (RawPack pack; pack.ParseFromArray(
                cache, static_cast<int>(offset + count))) {
                if (pack.type() == RAWPACKTYPE_CLIENT) {
                    m_type = NETWORK_NODE_TYPE_MOBILE;
                }
                if (pack.value().length() == pack.length()) {
                    m_network_manager->ProcMessage(static_cast<int>(pack.type()), this, pack.value());
                    retCode = PostRead(cache, 0);
                } else if (pack.value().length() < pack.length()) {
                    retCode = PostRead(cache, offset + count);
                } else {
                    std::string msg = pack.value();
                    int more = static_cast<int>(msg.length()) - pack.length();
                    #ifdef __MSC_VER
                    memcpy_s(cache, m_network_manager->cfg()->max_raw_pack_size,
                             msg.data() + pack.length(), more);
                    #else
                    memcpy(cache, msg.data() + pack.length(), more);
                    #endif
                    msg.resize(pack.length());
                    m_network_manager->ProcMessage(static_cast<int>(pack.type()), this, msg);
                    retCode = PostRead(cache, more);
                }
            }
            if (retCode != NETWORK_OK) {
                m_valid = false;
                RecycleReadBuf(cache);
            }
        }
    };

    m_socket->async_read_some(buffer(buf, m_network_manager->cfg()->max_raw_pack_size - offset), read_handler);
    return NETWORK_OK;
}
